package com.mini.auth.provider;

import com.mini.auth.authentication.SmsAuthenticationToken;
import com.mini.auth.constant.AuthConstant;
import com.mini.auth.service.UserDetailsServiceImpl;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;

/**
 * 自定义认证SmsAuthenticationToken的逻辑
 * 注意：不要 @Component 等注解将自定义Provider注入到容器中，否则将无法兼容 “授权码模式“，
 * 也就是当使用授权码模式时会出现无法找到 DaoAuthenticationProvider 的异常。
 *
 * @author Zhou Feng
 */
public class SmsAuthenticationProvider implements AuthenticationProvider {

    private UserDetailsService userDetailsService;
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String phone = authentication.getPrincipal().toString();
        String code = authentication.getCredentials().toString();
        if (checkSmsCode(phone, code)) {
            //认证成功
            UserDetails user = ((UserDetailsServiceImpl)userDetailsService).loadUserByMobile(phone);
            return new SmsAuthenticationToken(user, user.getPassword(), user.getAuthorities());
        }
        //认证失败
        return null;
    }

    /**
     * 校验短信验证码
     */
    private boolean checkSmsCode(String phone, String code) {
        if ("".equals(phone) || "".equals(code)) {
            return false;
        }
        String stored_code = String.valueOf(this.redisTemplate.opsForValue().get(AuthConstant.SMS_CODE_KEY_PREFIX + phone));
        return code.equals(stored_code);
    }

    //判断Authentication是否为SmsAuthenticationToken类型，以此来判断该provider是否可以认证SmsAuthenticationToken类型
    @Override
    public boolean supports(Class<?> authentication) {
        return SmsAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
}
